package com.tdk.mybatisplus.demo.common.util;


import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;


/**
 * <p>
 * bean转换工具类
 * </p>
 *
 * @author: taodingkai
 * @modified:
 * @since: 2021/11/26 17:52
 */
@Slf4j
public class TdkBeanUtils {

    public static final String EXCEPTION_MESSAGE = "执行异常 ";
    /**
     * 时间格式 yyyy-MM-dd HH:mm:ss
     */
    public static final String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    /**
     * 时间格式 yyyy-MM-dd'T'HH:mm:ss.SSS
     */
    public static final String TIME_PATTERN_WITH_WITH_MILLI_SECOND = "yyyy-MM-dd'T'HH:mm:ss.SSS";
    /**
     * ISO时间格式 yyyy-MM-dd'T'HH:mm:ss.SSSZ
     */
    public static final String ISO_TIME_PATTERN_WITH_MILLI_SECOND = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
    /**
     * 时间格式 yyyy-MM-dd HH:mm:ss.SSSSSS
     */
    public static final String TIME_PATTERN_WITH_NANO_SECOND = "yyyy-MM-dd HH:mm:ss.SSSSSS";
    /**
     * 时间格式 yyyy-MM-dd HH:mm:ss.SSSSSSZ
     */
    public static final String ISO_TIME_PATTERN_WITH_NANO_SECOND = "yyyy-MM-dd HH:mm:ss.SSSSSSZ";
    /**
     * 额外的转换器列表
     */
    public static List<CastNode> EXTRA_CAST_NODE_LIST = Lists.newArrayList();
    /**
     * 转换器
     */
    private static volatile CastNode CAST_NODE;

    /**
     * <p>
     * 复制属性值到对象，不支持级联
     * </p>
     *
     * @param description
     * @param source
     * @param target
     * @return T
     * @author: taodingkai
     * @since: 2021/8/25 11:26
     */
    public static <S, T> T copyValue(String description, S source, T target) {
        try {
            Class<?> sourceClass = source.getClass();
            if (Objects.isNull(sourceClass) || Map.class.isAssignableFrom(sourceClass)) {
                for (Map.Entry<String, Object> entry : ((Map<String, Object>) source).entrySet()) {
                    String key = entry.getKey();
                    Object value = entry.getValue();
                    setValue(description, target, key, value);
                }
            } else {
                Field[] sourceFieldArray = sourceClass.getDeclaredFields();
                for (int j = 0; j < sourceFieldArray.length; j++) {
                    Field sourceField = sourceFieldArray[j];
                    sourceField.setAccessible(true);
                    String key = sourceField.getName();
                    Object value = sourceField.get(source);
                    setValue(description, target, key, value);
                }
            }
        } catch (Exception e) {
            log.error(description + EXCEPTION_MESSAGE, e);
        }
        return target;
    }

    /**
     * <p>
     * 设置值
     * </p>
     *
     * @param description
     * @param target
     * @param key
     * @param value
     * @author: taodingkai
     * @since: 2021/8/25 11:38
     */
    public static <T> void setValue(
            String description, T target, String key, Object value) {
        if (Objects.nonNull(value) && Objects.nonNull(target) && Objects.nonNull(key)) {
            Field[] targetFieldArray = target.getClass().getDeclaredFields();
            try {
                loop:
                for (int k = 0; k < targetFieldArray.length; k++) {
                    Field targetField = targetFieldArray[k];
                    targetField.setAccessible(true);
                    if (!Modifier.isFinal(targetField.getModifiers()) || !Modifier
                            .isStatic(targetField.getModifiers())) {
                        // 去掉特殊字符忽略大小写比较
                        if (targetField.getName().replaceAll("[^0-9a-zA-Z]", "")
                                .equalsIgnoreCase(key.replaceAll("[^0-9a-zA-Z]", ""))) {
                            // 尝试转换
                            Object castResult = TdkBeanUtils.getCastNode().cast(targetField.getType(), value);
                            // 判断是否转换失败，失败打印日志
                            if (Objects.nonNull(value) && Objects.isNull(castResult)) {
                                // 非内置类型
                                log.error("类型:{},值:{}未找到转换器", target.getClass().getName(), value);
                            }
                            // 设置值
                            targetField.set(target, castResult);
                            //退出内层循环
                            break loop;
                        }
                    }
                }
            } catch (Exception e) {
                log.error(description + EXCEPTION_MESSAGE, e);
            }
        }
    }

    /**
     * <p>
     * 转换器 链表模式
     * </p>
     *
     * @author: taodingkai
     * @modified:
     * @since: 2021/11/23 15:55
     */
    static class CastNode {

        Class type;
        Function<Object, Object> function;
        CastNode next;
        List<CastNode> subList;

        public CastNode(
                Class type, Function<Object, Object> function,
                CastNode next,
                List<CastNode> subList) {
            this.type = type;
            this.function = function;
            this.next = next;
            this.subList = subList;
        }

        public CastNode(
                Class type, Function<Object, Object> function) {
            this.type = type;
            this.function = function;
        }

        public Class getType() {
            return type;
        }

        public void setType(Class type) {
            this.type = type;
        }

        public Function<Object, Object> getFunction() {
            return function;
        }

        public void setFunction(Function<Object, Object> function) {
            this.function = function;
        }

        public CastNode getNext() {
            return next;
        }

        public void setNext(CastNode next) {
            this.next = next;
        }

        public List<CastNode> getSubList() {
            return subList;
        }

        public void setSubList(
                List<CastNode> subList) {
            this.subList = subList;
        }

        /**
         * <p>
         * 转换
         * </p>
         *
         * @param targetClass
         * @param value
         * @return java.lang.Object
         * @author: taodingkai
         * @since: 2021/11/23 15:56
         */
        public Object cast(Class targetClass, Object value) {
            Object result = null;
            // 类型或者值有一个为null不做处理
            if (Objects.nonNull(targetClass) && Objects.nonNull(value)) {
                // 当前节点
                if (Objects.nonNull(this.getType()) && Objects.nonNull(this.getFunction())
                        && Objects.equals(targetClass.getName(), this.getType().getName())
                        && Objects.nonNull(value)) {
                    try {
                        result = this.getFunction().apply(value);
                    } catch (Exception e) {
                        log.debug("{}转换失败", value);
                    }
                }
                // 子节点
                if (Objects.isNull(result) && !ObjectUtils.isEmpty(this.getSubList())) {
                    result = this.getSubList().stream().map(castNode -> {
                        if (Objects.nonNull(castNode)) {
                            return castNode.cast(targetClass, value);
                        }
                        return null;
                    }).filter(Objects::nonNull).findFirst().orElse(null);
                }
                // 下一个节点
                if (Objects.isNull(result) && Objects.nonNull(this.getNext())) {
                    result = this.getNext().cast(targetClass, value);
                }
            }
            // 转换失败
            if (Objects.isNull(result) && Objects.nonNull(value)) {
                result = null;
            }
            return result;
        }
    }

    /**
     * <p>
     * 获取转换器
     * </p>
     *
     * @return com.tdk.mybatisplus.demo.common.util.TdkBeanUtils.CastNode
     * @author: taodingkai
     * @since: 2021/11/23 17:31
     */
    public static CastNode getCastNode() {
        if (Objects.isNull(CAST_NODE)) {
            synchronized (TdkBeanUtils.class) {
                if (Objects.isNull(CAST_NODE)) {
                    List<CastNode> castNodeList = Lists.newArrayList();
                    // 非时间类型转换
                    // String
                    castNodeList.add(new CastNode(String.class, (value) -> value.toString()));
                    // Boolean
                    castNodeList
                            .add(new CastNode(Boolean.class, (value) -> Boolean.valueOf(value.toString())));
                    // BigDecimal
                    castNodeList
                            .add(new CastNode(BigDecimal.class, (value) -> new BigDecimal(value.toString())));
                    // Long
                    castNodeList.add(
                            new CastNode(Long.class, (value) -> new BigDecimal(value.toString()).longValue()));
                    // Integer
                    castNodeList.add(
                            new CastNode(Integer.class, (value) -> new BigDecimal(value.toString()).intValue()));
                    // Short
                    castNodeList.add(
                            new CastNode(Short.class, (value) -> new BigDecimal(value.toString()).shortValue()));
                    // Byte
                    castNodeList.add(
                            new CastNode(Byte.class, (value) -> new BigDecimal(value.toString()).byteValue()));
                    // Double
                    castNodeList.add(new CastNode(Double.class,
                            (value) -> new BigDecimal(value.toString()).doubleValue()));

                    // 时间类型转换
                    // LocalDataTime

                    // String 转 LocalDataTime
                    List<CastNode> timeParseNodeList = Lists.newArrayList();
                    // 示例：'2011-12-03T10:15:30'
                    timeParseNodeList.add(new CastNode(LocalDateTime.class,
                            (time) -> LocalDateTime.parse(time.toString())));
                    // 示例：'2011-12-03T10:15:30Z'
                    timeParseNodeList.add(new CastNode(LocalDateTime.class,
                            (time) -> LocalDateTime.parse(time.toString(), DateTimeFormatter.ISO_INSTANT)));
                    // 示例：'2011-12-03T10:15:30' or '2011-12-03T10:15:30+01:00' or '2011-12-03T10:15:30+01:00[Europe/Paris]'
                    timeParseNodeList.add(new CastNode(LocalDateTime.class,
                            (time) -> LocalDateTime.parse(time.toString(), DateTimeFormatter.ISO_DATE_TIME)));
                    // 示例：'2011-12-03T10:15:30.000'
                    timeParseNodeList.add(new CastNode(LocalDateTime.class, (time) -> LocalDateTime
                            .parse(time.toString(),
                                    DateTimeFormatter.ofPattern(TIME_PATTERN_WITH_WITH_MILLI_SECOND))));
                    // 示例：'2011-12-03T10:15:30.000Z'
                    timeParseNodeList.add(new CastNode(LocalDateTime.class, (time) -> LocalDateTime
                            .parse(time.toString(),
                                    DateTimeFormatter.ofPattern(ISO_TIME_PATTERN_WITH_MILLI_SECOND))));
                    // 示例：'2011-12-03T10:15:30.000000'
                    timeParseNodeList.add(new CastNode(LocalDateTime.class, (time) -> LocalDateTime
                            .parse(time.toString(), DateTimeFormatter.ofPattern(TIME_PATTERN_WITH_NANO_SECOND))));
                    // 示例：'2011-12-03T10:15:30.000000Z'
                    timeParseNodeList.add(new CastNode(LocalDateTime.class, (time) -> LocalDateTime
                            .parse(time.toString(),
                                    DateTimeFormatter.ofPattern(ISO_TIME_PATTERN_WITH_NANO_SECOND))));
                    // 示例：'2011-12-03 10:15:30'
                    timeParseNodeList.add(new CastNode(LocalDateTime.class, (time) -> LocalDateTime
                            .parse(time.toString(), DateTimeFormatter.ofPattern(TIME_PATTERN))));
                    // 示例：'2011-12-03'
                    timeParseNodeList.add(new CastNode(LocalDateTime.class,
                            (time) -> LocalDateTime.parse(time.toString(), DateTimeFormatter.ISO_LOCAL_DATE)));
                    // 示例：'20111203'
                    timeParseNodeList.add(new CastNode(LocalDateTime.class,
                            (time) -> LocalDateTime.parse(time.toString(), DateTimeFormatter.BASIC_ISO_DATE)));
                    // list 转 链表
                    for (int index = 0; index < timeParseNodeList.size() - 1; index++) {
                        timeParseNodeList.get(index).setNext(timeParseNodeList.get(index + 1));
                    }
                    CastNode string2LocalDataTimeCastNode = timeParseNodeList.get(0);

                    // Long 转 LocalDataTime
                    CastNode long2LocalDataTimeCastNode = new CastNode(LocalDateTime.class,
                            (timestamp) -> LocalDateTime.ofInstant(
                                    Instant.ofEpochMilli(Long.valueOf(timestamp.toString())),
                                    ZoneId.systemDefault()));

                    // LocalDataTime
                    CastNode localDateTimeCastNode = new CastNode(LocalDateTime.class, (value) -> null);
                    List<CastNode> timeParseSubNodeList = Lists.newArrayList();
                    // Long 转 LocalDataTime
                    timeParseSubNodeList.add(long2LocalDataTimeCastNode);
                    // String 转 LocalDataTime
                    timeParseSubNodeList.add(string2LocalDataTimeCastNode);
                    localDateTimeCastNode.setSubList(timeParseSubNodeList);
                    castNodeList.add(localDateTimeCastNode);

                    // Date
                    CastNode dateCastNode = new CastNode(Date.class, (value) -> {
                        Object result = localDateTimeCastNode.cast(LocalDateTime.class, value);
                        if (result instanceof LocalDateTime) {
                            return Date.from(((LocalDateTime) result).atZone(ZoneId.systemDefault()).toInstant());
                        } else {
                            return null;
                        }
                    });
                    castNodeList.add(dateCastNode);

                    // 其他类型
                    castNodeList.addAll(EXTRA_CAST_NODE_LIST);

                    // list 转 链表
                    for (int index = 0; index < castNodeList.size() - 1; index++) {
                        castNodeList.get(index).setNext(castNodeList.get(index + 1));
                    }

                    // 赋值
                    CAST_NODE = castNodeList.get(0);
                }
            }
        }
        return CAST_NODE;
    }
}
